{---------------------------------------------------------------------
#
#	Apple Macintosh Developer Technical Support
#
#	MultiFinder-Aware Simple TextEdit Sample Application
#
#	OOPTESample
#
#	UTESample.inc1.p	-	Pascal Source
#
#	Copyright © 1988, 1989 Apple Computer, Inc.
#	All rights reserved.
#
#	Version:		
#					1.00					04/89
#
#	Components:     
#					BuildOOPTESample		April 1, 1989
#					MOOPTESample.p			April 1, 1989
#					OOPTESample.make		April 1, 1989
#					TECommon.h				April 1, 1989
#					TESampleGlue.a			April 1, 1989
#					TESample.r				April 1, 1989
#					UApplication.p			April 1, 1989
#					UApplication.inc1.p		April 1, 1989
#					UDocument.p				April 1, 1989
#					UDocument.inc1.p		April 1, 1989
#					UTEDocument.p			April 1, 1989
#					UTEDocument.inc1.p		April 1, 1989
#					UTESample.p				April 1, 1989
#					UTESample.inc1.p		April 1, 1989
#
---------------------------------------------------------------------}

CONST
	rMenuBar	= 128;
	rDocWindow	= 128;				{ application's window }
	rAboutAlert	= 128;				{ about alert }

	{ The following constants are used to identify menus and their items. The menu IDs }
	{ have an "m" prefix and the item numbers within each menu have an "i" prefix. }
	mApple		= 128;				{Apple menu}
	iAbout		= 1;

	mFile		= 129;				{ File menu }
	iNew		= 1;
	iClose		= 4;
	iQuit		= 12;

	mEdit		= 130;				{ Edit menu }
	iUndo		= 1;
	iCut		= 3;
	iCopy		= 4;
	iPaste		= 5;
	iClear		= 6;

	kMinSize		= 40;
	kMaxSleepTime	= 60;
	kSleepTime		= $7FFFFFFF;

	kExtremePos		= 32767 - 1;	{ required for old region bug }
	kExtremeNeg		= -32768;		{kExtremePos and kExtremeNeg are used to set up
									wide open rectangles and regions.}

	kMaxOpenDocuments		= 1;	{kMaxOpenDocuments is used to determine whether
									a new document can be opened or created. We keep
									track of the number of open documents, and disable
									the menu items that create a new document when the
									maximum is reached. If the number of documents falls
									below the maximum, the items are enabled again.}

(********************************************************************************************)
(*		T T E S a m p l e																	*)
(********************************************************************************************)
{$S Initialize}
{-----------------------------------+
|	ITESample						|
+-----------------------------------}
PROCEDURE TTESample.ITESample;
VAR
	menuBar: Handle;
BEGIN
	IApplication;
	
	{ Read menus into menubar }
	menuBar := GetNewMBar(rMenuBar);
	
	{ Install menus }
	SetMenuBar(menuBar);
	DisposHandle(menuBar);
	
	{ Add DA names to Apple menu }
	AddResMenu(GetMHandle(mApple),'DRVR');
	DrawMenuBar;
	
	{ Create empty mouse region }
	fMouseRgn := NewRgn;
	
	{ Create a single empty document }
	DoNew;
	
	{Make sure we have a valid cursor region }
	AdjustCursor;
END;

{$S Main}
{-----------------------------------+
|	DoIdle							|
+-----------------------------------}
{ This is called whenever we get a NULL event et al. }
{ It takes care of necessary periodic actions. For this program, }
{ it calls the DoIdle method of the current document. }
PROCEDURE TTESample.DoIdle; OVERRIDE;
VAR
	fTECurDoc: TTEDocument;
BEGIN
	fTECurdoc := TTEDocument(fCurDoc);
	IF fTECurDoc <> NIL THEN
		fTECurDoc.DoIdle;
END;

{$S Main}
{-----------------------------------+
|	AdjustMenus						|
+-----------------------------------}
{ Enable and disable menus based on the current state. The }
{ user can only select enabled menu items. We set up all the }
{ menu items before calling MenuSelect or MenuKey, since }
{ these are the only times that a menu item can be selected. }
{ Note that MenuSelect is also the only time the user will }
{ see menu items. This approach to deciding what enable/ }
{ disable state a menu item has the advantage of }
{ concentrating all the decision-making in one routine, as }
{ opposed to being spread throughout the application. Other }
{ application designs may take a different approach that may }
{ or may not be as valid.  }

PROCEDURE TTESample.AdjustMenus; OVERRIDE;
VAR
	frontMost:	WindowPtr;
	menu:		MenuHandle;
	offset:		longint;
	undo:		Boolean;
	cutCopyClear:	Boolean;
	paste:		Boolean;
	fTECurDoc:	TTEDocument;
BEGIN
	fTECurdoc := TTEDocument(fCurDoc);
	frontMost := FrontWindow;
	
	menu := GetMHandle(mFile);
	IF (fDocList.NumDocs < kMaxOpenDocuments) THEN
		EnableItem(menu,iNew)
	ELSE
		DisableItem(menu,iNew);
	IF (frontMost <> NIL) THEN
		EnableItem(menu,iClose)
	ELSE
		DisableItem(menu,iClose);
		
	menu := GetMHandle(mEdit);
	undo := FALSE;
	cutCopyClear := FALSE;
	paste := FALSE;
	IF (frontMost <> NIL) THEN 
		IF (fTECurDoc = NIL) THEN BEGIN		{ all editing is enabled for DA windows }
			undo := TRUE;
			cutCopyClear := TRUE;
			paste := TRUE;
		END ELSE BEGIN
			{ Cut, Copy, and Clear is enabled for app. windows with selections  }
			IF fTECurDoc.HaveSelection THEN
			  cutCopyClear := TRUE;
			{ If we have any TEXT in the scrap, enable paste }
			IF GetScrap(NIL, 'TEXT', offset) > 0 THEN
				paste := TRUE; 
		END;
	
	IF undo THEN
		EnableItem(menu,iUndo)
	ELSE
		DisableItem(menu,iUndo);
	IF cutCopyClear THEN BEGIN
		EnableItem(menu,iCut);
		EnableItem(menu,iCopy);
		EnableItem(menu,iClear);
	END ELSE BEGIN
		DisableItem(menu,iCut);
		DisableItem(menu,iCopy);
		DisableItem(menu,iClear);
	END;
	IF paste THEN
		EnableItem(menu,iPaste)
	ELSE
		DisableItem(menu,iPaste);
END;

{$S Main}
{-----------------------------------+
|	AdjustCursor					|
+-----------------------------------}
{ Change the cursor's shape, depending on its position. This also calculates a }
{ region that includes the cursor for WaitNextEvent. }
PROCEDURE TTESample.AdjustCursor; OVERRIDE;
VAR
	fTECurDoc:	TTEDocument;
	arrowRgn,
	iBeamRgn:	RgnHandle;
	mouse:		Point;
BEGIN
	fTECurdoc := TTEDocument(fCurDoc);
	
	{ Notice that we don't change the cursor if front window isn't ours }
	IF (NOT(fInBackGround) AND (fTECurDoc <> NIL)) THEN BEGIN
	
		GetMouse(mouse);
		LocalToGlobal(mouse);
		
		arrowRgn := NewRgn;
		iBeamRgn := NewRgn;
		
		SetRectRgn(arrowRgn,kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
		
		fTECurDoc.GetVisTERgn(iBeamRgn);
		
		DiffRgn(arrowRgn,iBeamRgn,arrowRgn);
		
		IF PtInRgn(mouse,iBeamRgn) THEN BEGIN
			SetCursor(GetCursor(iBeamCursor)^^);
			CopyRgn(iBeamRgn,fMouseRgn);
		END ELSE BEGIN
			SetCursor(arrow);
			CopyRgn(arrowRgn,fMouseRgn);
		END;
		DisposeRgn(arrowRgn);
		DisposeRgn(iBeamRgn);
	END;
END;

{$S Main}
{-----------------------------------+
|	DoMenuCommand					|
+-----------------------------------}
{ This is called when an item is chosen from the menu bar (after calling }
{ MenuSelect or MenuKey). It does the right thing for each command. }
PROCEDURE TTESample.DoMenuCommand(menuID,menuItem: integer); OVERRIDE;
VAR
	itemHit:	integer;
	daName:		Str255;
	daRefNum:	integer;
	window:		WindowPtr;
	fTECurDoc: TTEDocument;
BEGIN
	fTECurdoc := TTEDocument(fCurDoc);
	window := FrontWindow;
	
	CASE menuID OF
		mApple:
			CASE menuItem OF
				iAbout:					{ bring up alert for About  }
					itemHit := Alert(rAboutAlert, NIL);
				OTHERWISE BEGIN			{ all non-About items in this menu are DAs et al  }
					GetItem(GetMHandle(mApple), menuItem, daName);
					daRefNum := OpenDeskAcc(daName);
				END; { OTHERWISE }
			END; { CASE menuItem }
		mFile:
			CASE menuItem OF
				iNew:
					DoNew;
				iClose:
					IF fTECurDoc <> NIL THEN BEGIN
						fDocList.RemoveDoc(fTECurDoc);
						fTECurDoc.Free;
					END ELSE BEGIN
						CloseDeskAcc(WindowPeek(fWhichWindow)^.windowKind);
					END;
				iQuit:
					Terminate;
			END; { CASE menuItem }
		mEdit:					{ call SystemEdit for DA editing & MultiFinder  }
			IF NOT SystemEdit(menuItem-1) THEN
			 	CASE menuItem OF
					iCut:	fTECurDoc.DoCut;
					iCopy:	fTECurDoc.DoCopy;
					iPaste:	fTECurDoc.DoPaste;
					iClear:	fTECurDoc.DoClear;
				END; { CASE menuItem }
	END;
	HiliteMenu(0);
END; { doMenuCommand }

{$S Main}
{-----------------------------------+
|	HeapNeeded						|
+-----------------------------------}
FUNCTION TTESample.HeapNeeded: Longint; OVERRIDE;
BEGIN
	HeapNeeded := kMinSize * 1024;
END;

{$S Main}
{-----------------------------------+
|	SleepVal						|
+-----------------------------------}

{ Calculate a sleep value for WaitNextEvent. This takes into account the things }
{ that DoIdle does with Idle Time. The formula used here is: 
	IF we are in the foreground
	AND our document window is in front
	THEN
		SleepVal := whatever the front document says it should be
	ELSE
		SleepVal := aRealLongTime
		
	After that, SleepVal is clipped to kMaxSleepTime which is currently set
	to 60 ticks. This is to account for a Notification Manager bug wherein
	notifications don't occur while the front application is asleep. }

FUNCTION TTESample.SleepVal: LongInt; OVERRIDE;
VAR
	sleep: longint;
	fTECurDoc: TTEDocument;
BEGIN
	sleep := kSleepTime;
	fTECurdoc := TTEDocument(fCurDoc);
	IF ((NOT fInBackGround) AND (fTECurdoc <> NIL)) THEN BEGIN
		sleep := fTECurDoc.CalcIdle;
	END;
	IF sleep > kMaxSleepTime THEN
		sleep := kMaxSleepTime;
	SleepVal := sleep;
END;

{$S Initialize}
{-----------------------------------+
|	DoNew							|
+-----------------------------------}
{ Create a new document and window.  }
PROCEDURE TTESample.DoNew;
VAR
	tDoc:	TTEDocument;
BEGIN
	New(tdoc);
	tDoc.ITEDocument(rDocWindow);
	
	IF tdoc <> NIL THEN
		fdocList.Adddoc(tdoc);
END;

{$S Main}
{-----------------------------------+
|	Terminate						|
+-----------------------------------}
{ Clean up the application and exits. We close all of the windows so that }
{ they can update their documents. }
PROCEDURE TTESample.Terminate;
BEGIN
	{ FIX: close all docs }
	ExitLoop;
END;
